package org.cybzacg.convert.converters;

import org.cybzacg.convert.annotation.Converter;
import org.cybzacg.convert.context.ConversionContext;
import org.cybzacg.convert.core.AbstractTypeConverter;
import org.cybzacg.convert.core.TypeConverter;
import org.cybzacg.convert.enums.ConversionError;
import org.cybzacg.convert.enums.ConversionPriority;
import org.cybzacg.convert.enums.ConversionType;
import org.cybzacg.convert.exception.ConversionException;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通用类型转换器
 * 提供各种类型之间的通用转换功能，支持基本类型、包装类型、字符串、日期时间、集合等
 *
 * @author cybzacg.blog
 * @version 3.0
 * @since 3.0
 */
@Slf4j
@Converter(
    name = "UniversalTypeConverter",
    priority = ConversionPriority.UNIVERSAL,
    description = "通用类型转换器，支持各种类型之间的转换",
    enabled = true,
    version = "3.1",
    author = "cybzacg.blog",
    supportedTypes = {Object.class},
    tags = {"universal", "general", "conversion", "type"}
)
public class UniversalTypeConverter extends AbstractTypeConverter {

    /**
     * 类型转换器缓存
     */
    private final Map<String, TypeConverter> converterCache = new ConcurrentHashMap<>();

    /**
     * 基本类型映射
     */
    private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP = new HashMap<>();

    static {
        PRIMITIVE_WRAPPER_MAP.put(boolean.class, Boolean.class);
        PRIMITIVE_WRAPPER_MAP.put(byte.class, Byte.class);
        PRIMITIVE_WRAPPER_MAP.put(char.class, Character.class);
        PRIMITIVE_WRAPPER_MAP.put(short.class, Short.class);
        PRIMITIVE_WRAPPER_MAP.put(int.class, Integer.class);
        PRIMITIVE_WRAPPER_MAP.put(long.class, Long.class);
        PRIMITIVE_WRAPPER_MAP.put(float.class, Float.class);
        PRIMITIVE_WRAPPER_MAP.put(double.class, Double.class);
    }

    public UniversalTypeConverter() {
        super(ConversionPriority.UNIVERSAL);
        log.debug("初始化通用类型转换器");
    }

    @Override
    protected ConversionType getConversionType() {
        return ConversionType.OBJECT;
    }

    @Override
    protected List<ConversionType> getSupportedConversionTypes() {
        return Arrays.asList(ConversionType.values());
    }

    @Override
    @SuppressWarnings("unchecked")
    protected <T> T doConvert(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {

        if (source == null) {
            return handleNullValue(targetType, defaultValue, context);
        }

        Class<?> sourceType = source.getClass();

        // 如果源类型和目标类型相同，直接返回
        if (targetType.isAssignableFrom(sourceType)) {
            return (T) source;
        }

        // 处理数组类型
        if (targetType.isArray()) {
            return convertToArray(source, targetType, defaultValue, context);
        }

        // 处理集合类型
        if (Collection.class.isAssignableFrom(targetType)) {
            return convertToCollection(source, targetType, defaultValue, context);
        }

        // 处理Map类型
        if (Map.class.isAssignableFrom(targetType)) {
            return convertToMap(source, targetType, defaultValue, context);
        }

        // 处理基本类型和包装类型
        if (isPrimitiveOrWrapper(targetType)) {
            return convertToPrimitive(source, targetType, defaultValue, context);
        }

        // 处理字符串类型
        if (targetType == String.class) {
            return convertToString(source, context);
        }

        // 处理数字类型
        if (Number.class.isAssignableFrom(targetType)) {
            return convertToNumber(source, targetType, defaultValue, context);
        }

        // 处理日期时间类型
        if (isDateTimeType(targetType)) {
            return convertToDateTime(source, targetType, defaultValue, context);
        }

        // 处理枚举类型
        if (targetType.isEnum()) {
            return convertToEnum(source, targetType, defaultValue, context);
        }

        // 尝试通过构造函数转换
        return convertByConstructor(source, targetType, defaultValue, context);
    }

    /**
     * 处理null值
     */
    @SuppressWarnings("unchecked")
    private <T> T handleNullValue(Class<T> targetType, T defaultValue, ConversionContext context) {
        if (defaultValue != null) {
            return defaultValue;
        }

        // 对于基本类型，返回默认值
        if (targetType.isPrimitive()) {
            return (T) getPrimitiveDefault(targetType);
        }

        return null;
    }

    /**
     * 获取基本类型的默认值
     */
    private Object getPrimitiveDefault(Class<?> primitiveType) {
        if (primitiveType == boolean.class) return false;
        if (primitiveType == byte.class) return (byte) 0;
        if (primitiveType == char.class) return '\0';
        if (primitiveType == short.class) return (short) 0;
        if (primitiveType == int.class) return 0;
        if (primitiveType == long.class) return 0L;
        if (primitiveType == float.class) return 0.0f;
        if (primitiveType == double.class) return 0.0d;
        return null;
    }

    /**
     * 转换为数组
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToArray(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        Class<?> componentType = targetType.getComponentType();

        if (source.getClass().isArray()) {
            // 数组到数组的转换
            int length = Array.getLength(source);
            Object result = Array.newInstance(componentType, length);

            for (int i = 0; i < length; i++) {
                Object element = Array.get(source, i);
                Object convertedElement = convert(element, componentType);
                Array.set(result, i, convertedElement);
            }

            return (T) result;
        } else if (source instanceof Collection) {
            // 集合到数组的转换
            Collection<?> collection = (Collection<?>) source;
            Object result = Array.newInstance(componentType, collection.size());

            int i = 0;
            for (Object element : collection) {
                Object convertedElement = convert(element, componentType);
                Array.set(result, i++, convertedElement);
            }

            return (T) result;
        } else {
            // 单个对象到数组的转换
            Object result = Array.newInstance(componentType, 1);
            Object convertedElement = convert(source, componentType);
            Array.set(result, 0, convertedElement);
            return (T) result;
        }
    }

    /**
     * 转换为集合
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToCollection(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        Collection<Object> result;

        try {
            result = (Collection<Object>) targetType.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            // 如果无法实例化，使用ArrayList
            result = new ArrayList<>();
        }

        if (source.getClass().isArray()) {
            // 数组到集合的转换
            int length = Array.getLength(source);
            for (int i = 0; i < length; i++) {
                Object element = Array.get(source, i);
                result.add(element);
            }
        } else if (source instanceof Collection) {
            // 集合到集合的转换
            result.addAll((Collection<?>) source);
        } else {
            // 单个对象到集合的转换
            result.add(source);
        }

        return (T) result;
    }

    /**
     * 转换为Map
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToMap(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        Map<Object, Object> result;

        try {
            result = (Map<Object, Object>) targetType.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            // 如果无法实例化，使用HashMap
            result = new HashMap<>();
        }

        if (source instanceof Map) {
            // Map到Map的转换
            result.putAll((Map<?, ?>) source);
        } else {
            // 其他类型转换为Map
            result.put("value", source);
        }

        return (T) result;
    }

    /**
     * 转换为基本类型或包装类型
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToPrimitive(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        Class<?> wrapperType = PRIMITIVE_WRAPPER_MAP.getOrDefault(targetType, targetType);

        if (source instanceof Number number) {

            if (wrapperType == Boolean.class) {
                return (T) Boolean.valueOf(number.intValue() != 0);
            } else if (wrapperType == Byte.class) {
                return (T) Byte.valueOf(number.byteValue());
            } else if (wrapperType == Character.class) {
                return (T) Character.valueOf((char) number.intValue());
            } else if (wrapperType == Short.class) {
                return (T) Short.valueOf(number.shortValue());
            } else if (wrapperType == Integer.class) {
                return (T) Integer.valueOf(number.intValue());
            } else if (wrapperType == Long.class) {
                return (T) Long.valueOf(number.longValue());
            } else if (wrapperType == Float.class) {
                return (T) Float.valueOf(number.floatValue());
            } else if (wrapperType == Double.class) {
                return (T) Double.valueOf(number.doubleValue());
            }
        } else if (source instanceof Boolean) {
            Boolean bool = (Boolean) source;

            if (wrapperType == Boolean.class) {
                return (T) bool;
            } else if (wrapperType == Byte.class) {
                return (T) Byte.valueOf(bool ? (byte) 1 : (byte) 0);
            } else if (wrapperType == Character.class) {
                return (T) Character.valueOf(bool ? '1' : '0');
            } else if (wrapperType == Short.class) {
                return (T) Short.valueOf(bool ? (short) 1 : (short) 0);
            } else if (wrapperType == Integer.class) {
                return (T) Integer.valueOf(bool ? 1 : 0);
            } else if (wrapperType == Long.class) {
                return (T) Long.valueOf(bool ? 1L : 0L);
            } else if (wrapperType == Float.class) {
                return (T) Float.valueOf(bool ? 1.0f : 0.0f);
            } else if (wrapperType == Double.class) {
                return (T) Double.valueOf(bool ? 1.0 : 0.0);
            }
        } else if (source instanceof Character) {
            Character ch = (Character) source;

            if (wrapperType == Boolean.class) {
                return (T) Boolean.valueOf(ch != '0' && ch != '\0');
            } else if (wrapperType == Byte.class) {
                return (T) Byte.valueOf((byte) ch.charValue());
            } else if (wrapperType == Character.class) {
                return (T) ch;
            } else if (wrapperType == Short.class) {
                return (T) Short.valueOf((short) ch.charValue());
            } else if (wrapperType == Integer.class) {
                return (T) Integer.valueOf((int) ch.charValue());
            } else if (wrapperType == Long.class) {
                return (T) Long.valueOf((long) ch.charValue());
            } else if (wrapperType == Float.class) {
                return (T) Float.valueOf((float) ch.charValue());
            } else if (wrapperType == Double.class) {
                return (T) Double.valueOf((double) ch.charValue());
            }
        }

        // 尝试通过字符串转换
        String strValue = source.toString().trim();
        if (strValue.isEmpty()) {
            return defaultValue;
        }

        try {
            if (wrapperType == Boolean.class) {
                // 处理布尔值的多种表示形式
                if ("true".equalsIgnoreCase(strValue) || "1".equals(strValue)) {
                    return (T) Boolean.TRUE;
                } else if ("false".equalsIgnoreCase(strValue) || "0".equals(strValue)) {
                    return (T) Boolean.FALSE;
                } else {
                    return (T) Boolean.valueOf(strValue);
                }
            } else if (wrapperType == Byte.class) {
                return (T) Byte.valueOf(strValue);
            } else if (wrapperType == Character.class) {
                if (strValue.length() > 0) {
                    return (T) Character.valueOf(strValue.charAt(0));
                } else {
                    return (T) Character.valueOf('\0');
                }
            } else if (wrapperType == Short.class) {
                return (T) Short.valueOf(strValue);
            } else if (wrapperType == Integer.class) {
                return (T) Integer.valueOf(strValue);
            } else if (wrapperType == Long.class) {
                return (T) Long.valueOf(strValue);
            } else if (wrapperType == Float.class) {
                return (T) Float.valueOf(strValue);
            } else if (wrapperType == Double.class) {
                return (T) Double.valueOf(strValue);
            }
        } catch (NumberFormatException e) {
            throw new ConversionException(ConversionError.TYPE_MISMATCH,
                "无法将字符串 '" + strValue + "' 转换为 " + targetType.getSimpleName(), e);
        }

        return defaultValue;
    }

    /**
     * 转换为字符串
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToString(Object source, ConversionContext context) {
        String result;

        if (source == null) {
            result = "";
        } else if (source.getClass().isArray()) {
            result = arrayToString(source);
        } else if (source instanceof Collection) {
            result = collectionToString((Collection<?>) source);
        } else if (source instanceof Map) {
            result = mapToString((Map<?, ?>) source);
        } else {
            result = source.toString();
        }

        return (T) result;
    }

    /**
     * 转换为数字类型
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToNumber(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        if (source instanceof Number number) {

            if (targetType == BigDecimal.class) {
                return (T) BigDecimal.valueOf(number.doubleValue());
            } else if (targetType == BigInteger.class) {
                return (T) BigInteger.valueOf(number.longValue());
            } else if (targetType == Byte.class) {
                return (T) Byte.valueOf(number.byteValue());
            } else if (targetType == Short.class) {
                return (T) Short.valueOf(number.shortValue());
            } else if (targetType == Integer.class) {
                return (T) Integer.valueOf(number.intValue());
            } else if (targetType == Long.class) {
                return (T) Long.valueOf(number.longValue());
            } else if (targetType == Float.class) {
                return (T) Float.valueOf(number.floatValue());
            } else if (targetType == Double.class) {
                return (T) Double.valueOf(number.doubleValue());
            }
        }

        // 尝试通过字符串转换
        String strValue = source.toString().trim();
        if (strValue.isEmpty()) {
            return defaultValue;
        }

        try {
            if (targetType == BigDecimal.class) {
                return (T) new BigDecimal(strValue);
            } else if (targetType == BigInteger.class) {
                return (T) new BigInteger(strValue);
            } else if (targetType == Byte.class) {
                return (T) Byte.valueOf(strValue);
            } else if (targetType == Short.class) {
                return (T) Short.valueOf(strValue);
            } else if (targetType == Integer.class) {
                return (T) Integer.valueOf(strValue);
            } else if (targetType == Long.class) {
                return (T) Long.valueOf(strValue);
            } else if (targetType == Float.class) {
                return (T) Float.valueOf(strValue);
            } else if (targetType == Double.class) {
                return (T) Double.valueOf(strValue);
            }
        } catch (NumberFormatException e) {
            throw new ConversionException(ConversionError.TYPE_MISMATCH,
                "无法将字符串 '" + strValue + "' 转换为 " + targetType.getSimpleName(), e);
        }

        return defaultValue;
    }

    /**
     * 转换为日期时间类型
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToDateTime(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        if (source instanceof Long) {
            Long timestamp = (Long) source;

            if (targetType == Date.class) {
                return (T) new Date(timestamp);
            } else if (targetType == Instant.class) {
                return (T) Instant.ofEpochMilli(timestamp);
            } else if (targetType == LocalDateTime.class) {
                return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
            } else if (targetType == LocalDate.class) {
                return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).toLocalDate();
            } else if (targetType == LocalTime.class) {
                return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).toLocalTime();
            } else if (targetType == ZonedDateTime.class) {
                return (T) ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
            }
        } else if (source instanceof String) {
            String strValue = ((String) source).trim();
            if (strValue.isEmpty()) {
                return defaultValue;
            }

            try {
                if (targetType == Date.class) {
                    return (T) new Date(Long.parseLong(strValue));
                } else if (targetType == Instant.class) {
                    return (T) Instant.parse(strValue);
                } else if (targetType == LocalDateTime.class) {
                    return (T) LocalDateTime.parse(strValue);
                } else if (targetType == LocalDate.class) {
                    return (T) LocalDate.parse(strValue);
                } else if (targetType == LocalTime.class) {
                    return (T) LocalTime.parse(strValue);
                } else if (targetType == ZonedDateTime.class) {
                    return (T) ZonedDateTime.parse(strValue);
                }
            } catch (Exception e) {
                throw new ConversionException(ConversionError.TYPE_MISMATCH,
                    "无法将字符串 '" + strValue + "' 转换为 " + targetType.getSimpleName(), e);
            }
        }

        return defaultValue;
    }

    /**
     * 转换为枚举类型
     */
    @SuppressWarnings("unchecked")
    private <T> T convertToEnum(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        if (!targetType.isEnum()) {
            return defaultValue;
        }

        String strValue = source.toString().trim();
        if (strValue.isEmpty()) {
            return defaultValue;
        }

        try {
            return (T) Enum.valueOf((Class<? extends Enum>) targetType, strValue);
        } catch (IllegalArgumentException e) {
            throw new ConversionException(ConversionError.TYPE_MISMATCH,
                "无法将值 '" + strValue + "' 转换为枚举 " + targetType.getSimpleName(), e);
        }
    }

    /**
     * 通过构造函数转换
     */
    @SuppressWarnings("unchecked")
    private <T> T convertByConstructor(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {
        try {
            // 尝试查找接受String参数的构造函数
            try {
                var constructor = targetType.getConstructor(String.class);
                return (T) constructor.newInstance(source.toString());
            } catch (NoSuchMethodException e) {
                // 尝试查找接受源类型参数的构造函数
                try {
                    var constructor = targetType.getConstructor(source.getClass());
                    return (T) constructor.newInstance(source);
                } catch (NoSuchMethodException e2) {
                    // 无法找到合适的构造函数
                    throw new ConversionException(ConversionError.TYPE_MISMATCH,
                        "无法将 " + source.getClass().getSimpleName() + " 转换为 " + targetType.getSimpleName());
                }
            }
        } catch (Exception e) {
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                "转换失败: " + e.getMessage(), e);
        }
    }

    /**
     * 检查是否为基本类型或包装类型
     */
    private boolean isPrimitiveOrWrapper(Class<?> type) {
        return type.isPrimitive() || PRIMITIVE_WRAPPER_MAP.containsValue(type);
    }

    /**
     * 检查是否为日期时间类型
     */
    private boolean isDateTimeType(Class<?> type) {
        return type == Date.class ||
               type == Instant.class ||
               type == LocalDateTime.class ||
               type == LocalDate.class ||
               type == LocalTime.class ||
               type == ZonedDateTime.class ||
               type == Year.class ||
               type == YearMonth.class ||
               type == Period.class ||
               type == Duration.class;
    }

    /**
     * 数组转字符串
     */
    private String arrayToString(Object array) {
        int length = Array.getLength(array);
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < length; i++) {
            if (i > 0) {
                sb.append(", ");
            }
            sb.append(Array.get(array, i));
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * 集合转字符串
     */
    private String collectionToString(Collection<?> collection) {
        StringBuilder sb = new StringBuilder("[");
        boolean first = true;
        for (Object item : collection) {
            if (!first) {
                sb.append(", ");
            }
            sb.append(item);
            first = false;
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * Map转字符串
     */
    private String mapToString(Map<?, ?> map) {
        StringBuilder sb = new StringBuilder("{");
        boolean first = true;
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            if (!first) {
                sb.append(", ");
            }
            sb.append(entry.getKey()).append("=").append(entry.getValue());
            first = false;
        }
        sb.append("}");
        return sb.toString();
    }

    @Override
    public Class<?> getSupportedType() {
        return Object.class;
    }
}
